/*
 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */
package javax.swing.text;

import java.awt.*;
import javax.swing.SwingConstants;
import javax.swing.event.*;

/**
 * <p> A very important part of the text package is the <code>View</code> class. As the name
 * suggests it represents a view of the text model, or a piece of the text model. It is this class
 * that is responsible for the look of the text component. The view is not intended to be some
 * completely new thing that one must learn, but rather is much like a lightweight component. <p> By
 * default, a view is very light.  It contains a reference to the parent view from which it can
 * fetch many things without holding state, and it contains a reference to a portion of the model
 * (<code>Element</code>). A view does not have to exactly represent an element in the model, that
 * is simply a typical and therefore convenient mapping.  A view can alternatively maintain a couple
 * of Position objects to maintain its location in the model (i.e. represent a fragment of an
 * element).  This is typically the result of formatting where views have been broken down into
 * pieces.  The convenience of a substantial relationship to the element makes it easier to build
 * factories to produce the views, and makes it easier  to keep track of the view pieces as the
 * model is changed and the view must be changed to reflect the model.  Simple views therefore
 * represent an Element directly and complex views do not. <p> A view has the following
 * responsibilities: <dl>
 *
 * <dt><b>Participate in layout.</b> <dd> <p>The view has a <code>setSize</code> method which is
 * like <code>doLayout</code> and <code>setSize</code> in <code>Component</code> combined. The view
 * has a <code>preferenceChanged</code> method which is like <code>invalidate</code> in
 * <code>Component</code> except that one can invalidate just one axis and the child requesting the
 * change is identified. <p>A View expresses the size that it would like to be in terms of three
 * values, a minimum, a preferred, and a maximum span.  Layout in a view is can be done
 * independently upon each axis.  For a properly functioning View implementation, the minimum span
 * will be &lt;= the preferred span which in turn will be &lt;= the maximum span. </p> <p
 * style="text-align:center"><img src="doc-files/View-flexibility.jpg" alt="The above text describes
 * this graphic."> <p>The minimum set of methods for layout are: <ul> <li>{@link
 * #getMinimumSpan(int) getMinimumSpan} <li>{@link #getPreferredSpan(int) getPreferredSpan}
 * <li>{@link #getMaximumSpan(int) getMaximumSpan} <li>{@link #getAlignment(int) getAlignment}
 * <li>{@link #preferenceChanged(javax.swing.text.View, boolean, boolean) preferenceChanged}
 * <li>{@link #setSize(float, float) setSize} </ul>
 *
 * <p>The <code>setSize</code> method should be prepared to be called a number of times (i.e. It may
 * be called even if the size didn't change). The <code>setSize</code> method is generally called to
 * make sure the View layout is complete prior to trying to perform an operation on it that requires
 * an up-to-date layout.  A view's size should <em>always</em> be set to a value within the minimum
 * and maximum span specified by that view.  Additionally, the view must always call the
 * <code>preferenceChanged</code> method on the parent if it has changed the values for the layout
 * it would like, and expects the parent to honor.  The parent View is not required to recognize a
 * change until the <code>preferenceChanged</code> has been sent. This allows parent View
 * implementations to cache the child requirements if desired.  The calling sequence looks something
 * like the following: </p> <p style="text-align:center"> <img src="doc-files/View-layout.jpg"
 * alt="Sample calling sequence between parent view and child view: setSize, getMinimum,
 * getPreferred, getMaximum, getAlignment, setSize"> <p>The exact calling sequence is up to the
 * layout functionality of the parent view (if the view has any children).  The view may collect the
 * preferences of the children prior to determining what it will give each child, or it might
 * iteratively update the children one at a time. </p>
 *
 * <dt><b>Render a portion of the model.</b> <dd> <p>This is done in the paint method, which is
 * pretty much like a component paint method.  Views are expected to potentially populate a fairly
 * large tree.  A <code>View</code> has the following semantics for rendering: </p> <ul> <li>The
 * view gets its allocation from the parent at paint time, so it must be prepared to redo layout if
 * the allocated area is different from what it is prepared to deal with. <li>The coordinate system
 * is the same as the hosting <code>Component</code> (i.e. the <code>Component</code> returned by
 * the {@link #getContainer getContainer} method). This means a child view lives in the same
 * coordinate system as the parent view unless the parent has explicitly changed the coordinate
 * system. To schedule itself to be repainted a view can call repaint on the hosting
 * <code>Component</code>. <li>The default is to <em>not clip</em> the children.  It is more
 * efficient to allow a view to clip only if it really feels it needs clipping. <li>The
 * <code>Graphics</code> object given is not initialized in any way. A view should set any settings
 * needed. <li>A <code>View</code> is inherently transparent.  While a view may render into its
 * entire allocation, typically a view does not.  Rendering is performed by traversing down the tree
 * of <code>View</code> implementations. Each <code>View</code> is responsible for rendering its
 * children.  This behavior is depended upon for thread safety.  While view implementations do not
 * necessarily have to be implemented with thread safety in mind, other view implementations that do
 * make use of concurrency can depend upon a tree traversal to guarantee thread safety. <li>The
 * order of views relative to the model is up to the implementation. Although child views will
 * typically be arranged in the same order that they occur in the model, they may be visually
 * arranged in an entirely different order.  View implementations may have Z-Order associated with
 * them if the children are overlapping. </ul> <p>The methods for rendering are: <ul> <li>{@link
 * #paint(java.awt.Graphics, java.awt.Shape) paint} </ul>
 *
 * <dt><b>Translate between the model and view coordinate systems.</b> <dd> <p>Because the view
 * objects are produced from a factory and therefore cannot necessarily be counted upon to be in a
 * particular pattern, one must be able to perform translation to properly locate spatial
 * representation of the model. The methods for doing this are: <ul> <li>{@link #modelToView(int,
 * javax.swing.text.Position.Bias, int, javax.swing.text.Position.Bias, java.awt.Shape) modelToView}
 * <li>{@link #viewToModel(float, float, java.awt.Shape, javax.swing.text.Position.Bias[])
 * viewToModel} <li>{@link #getDocument() getDocument} <li>{@link #getElement() getElement}
 * <li>{@link #getStartOffset() getStartOffset} <li>{@link #getEndOffset() getEndOffset} </ul>
 * <p>The layout must be valid prior to attempting to make the translation. The translation is not
 * valid, and must not be attempted while changes are being broadcasted from the model via a
 * <code>DocumentEvent</code>. </p>
 *
 * <dt><b>Respond to changes from the model.</b> <dd> <p>If the overall view is represented by many
 * pieces (which is the best situation if one want to be able to change the view and write the least
 * amount of new code), it would be impractical to have a huge number of
 * <code>DocumentListener</code>s. If each view listened to the model, only a few would actually be
 * interested in the changes broadcasted at any given time.   Since the model has no knowledge of
 * views, it has no way to filter the broadcast of change information.  The view hierarchy itself is
 * instead responsible for propagating the change information. At any level in the view hierarchy,
 * that view knows enough about its children to best distribute the change information further.
 * Changes are therefore broadcasted starting from the root of the view hierarchy. The methods for
 * doing this are: <ul> <li>{@link #insertUpdate insertUpdate} <li>{@link #removeUpdate
 * removeUpdate} <li>{@link #changedUpdate changedUpdate} </ul> <p> </dl>
 *
 * @author Timothy Prinzing
 */
public abstract class View implements SwingConstants {

  /**
   * Creates a new <code>View</code> object.
   *
   * @param elem the <code>Element</code> to represent
   */
  public View(Element elem) {
    this.elem = elem;
  }

  /**
   * Returns the parent of the view.
   *
   * @return the parent, or <code>null</code> if none exists
   */
  public View getParent() {
    return parent;
  }

  /**
   * Returns a boolean that indicates whether
   * the view is visible or not.  By default
   * all views are visible.
   *
   * @return always returns true
   */
  public boolean isVisible() {
    return true;
  }


  /**
   * Determines the preferred span for this view along an
   * axis.
   *
   * @param axis may be either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
   * @return the span the view would like to be rendered into. Typically the view is told to render
   * into the span that is returned, although there is no guarantee. The parent may choose to resize
   * or break the view
   * @see View#getPreferredSpan
   */
  public abstract float getPreferredSpan(int axis);

  /**
   * Determines the minimum span for this view along an
   * axis.
   *
   * @param axis may be either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
   * @return the minimum span the view can be rendered into
   * @see View#getPreferredSpan
   */
  public float getMinimumSpan(int axis) {
    int w = getResizeWeight(axis);
    if (w == 0) {
      // can't resize
      return getPreferredSpan(axis);
    }
    return 0;
  }

  /**
   * Determines the maximum span for this view along an
   * axis.
   *
   * @param axis may be either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
   * @return the maximum span the view can be rendered into
   * @see View#getPreferredSpan
   */
  public float getMaximumSpan(int axis) {
    int w = getResizeWeight(axis);
    if (w == 0) {
      // can't resize
      return getPreferredSpan(axis);
    }
    return Integer.MAX_VALUE;
  }

  /**
   * Child views can call this on the parent to indicate that
   * the preference has changed and should be reconsidered
   * for layout.  By default this just propagates upward to
   * the next parent.  The root view will call
   * <code>revalidate</code> on the associated text component.
   *
   * @param child the child view
   * @param width true if the width preference has changed
   * @param height true if the height preference has changed
   * @see javax.swing.JComponent#revalidate
   */
  public void preferenceChanged(View child, boolean width, boolean height) {
    View parent = getParent();
    if (parent != null) {
      parent.preferenceChanged(this, width, height);
    }
  }

  /**
   * Determines the desired alignment for this view along an
   * axis.  The desired alignment is returned.  This should be
   * a value &gt;= 0.0 and &lt;= 1.0, where 0 indicates alignment at
   * the origin and 1.0 indicates alignment to the full span
   * away from the origin.  An alignment of 0.5 would be the
   * center of the view.
   *
   * @param axis may be either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
   * @return the value 0.5
   */
  public float getAlignment(int axis) {
    return 0.5f;
  }

  /**
   * Renders using the given rendering surface and area on that
   * surface.  The view may need to do layout and create child
   * views to enable itself to render into the given allocation.
   *
   * @param g the rendering surface to use
   * @param allocation the allocated region to render into
   */
  public abstract void paint(Graphics g, Shape allocation);

  /**
   * Establishes the parent view for this view.  This is
   * guaranteed to be called before any other methods if the
   * parent view is functioning properly.  This is also
   * the last method called, since it is called to indicate
   * the view has been removed from the hierarchy as
   * well. When this method is called to set the parent to
   * null, this method does the same for each of its children,
   * propagating the notification that they have been
   * disconnected from the view tree. If this is
   * reimplemented, <code>super.setParent()</code> should
   * be called.
   *
   * @param parent the new parent, or <code>null</code> if the view is being removed from a parent
   */
  public void setParent(View parent) {
    // if the parent is null then propogate down the view tree
    if (parent == null) {
      for (int i = 0; i < getViewCount(); i++) {
        if (getView(i).getParent() == this) {
          // in FlowView.java view might be referenced
          // from two super-views as a child. see logicalView
          getView(i).setParent(null);
        }
      }
    }
    this.parent = parent;
  }

  /**
   * Returns the number of views in this view.  Since
   * the default is to not be a composite view this
   * returns 0.
   *
   * @return the number of views &gt;= 0
   * @see View#getViewCount
   */
  public int getViewCount() {
    return 0;
  }

  /**
   * Gets the <i>n</i>th child view.  Since there are no
   * children by default, this returns <code>null</code>.
   *
   * @param n the number of the view to get, &gt;= 0 &amp;&amp; &lt; getViewCount()
   * @return the view
   */
  public View getView(int n) {
    return null;
  }


  /**
   * Removes all of the children.  This is a convenience
   * call to <code>replace</code>.
   *
   * @since 1.3
   */
  public void removeAll() {
    replace(0, getViewCount(), null);
  }

  /**
   * Removes one of the children at the given position.
   * This is a convenience call to <code>replace</code>.
   *
   * @since 1.3
   */
  public void remove(int i) {
    replace(i, 1, null);
  }

  /**
   * Inserts a single child view.  This is a convenience
   * call to <code>replace</code>.
   *
   * @param offs the offset of the view to insert before &gt;= 0
   * @param v the view
   * @see #replace
   * @since 1.3
   */
  public void insert(int offs, View v) {
    View[] one = new View[1];
    one[0] = v;
    replace(offs, 0, one);
  }

  /**
   * Appends a single child view.  This is a convenience
   * call to <code>replace</code>.
   *
   * @param v the view
   * @see #replace
   * @since 1.3
   */
  public void append(View v) {
    View[] one = new View[1];
    one[0] = v;
    replace(getViewCount(), 0, one);
  }

  /**
   * Replaces child views.  If there are no views to remove
   * this acts as an insert.  If there are no views to
   * add this acts as a remove.  Views being removed will
   * have the parent set to <code>null</code>, and the internal reference
   * to them removed so that they can be garbage collected.
   * This is implemented to do nothing, because by default
   * a view has no children.
   *
   * @param offset the starting index into the child views to insert the new views.  This should be
   * a value &gt;= 0 and &lt;= getViewCount
   * @param length the number of existing child views to remove This should be a value &gt;= 0 and
   * &lt;= (getViewCount() - offset).
   * @param views the child views to add.  This value can be <code>null</code> to indicate no
   * children are being added (useful to remove).
   * @since 1.3
   */
  public void replace(int offset, int length, View[] views) {
  }

  /**
   * Returns the child view index representing the given position in
   * the model.  By default a view has no children so this is implemented
   * to return -1 to indicate there is no valid child index for any
   * position.
   *
   * @param pos the position &gt;= 0
   * @return index of the view representing the given position, or -1 if no view represents that
   * position
   * @since 1.3
   */
  public int getViewIndex(int pos, Position.Bias b) {
    return -1;
  }

  /**
   * Fetches the allocation for the given child view.
   * This enables finding out where various views
   * are located, without assuming how the views store
   * their location.  This returns <code>null</code> since the
   * default is to not have any child views.
   *
   * @param index the index of the child, &gt;= 0 &amp;&amp; &lt; <code>getViewCount()</code>
   * @param a the allocation to this view
   * @return the allocation to the child
   */
  public Shape getChildAllocation(int index, Shape a) {
    return null;
  }

  /**
   * Provides a way to determine the next visually represented model
   * location at which one might place a caret.
   * Some views may not be visible,
   * they might not be in the same order found in the model, or they just
   * might not allow access to some of the locations in the model.
   * This method enables specifying a position to convert
   * within the range of &gt;=0.  If the value is -1, a position
   * will be calculated automatically.  If the value &lt; -1,
   * the {@code BadLocationException} will be thrown.
   *
   * @param pos the position to convert
   * @param a the allocated region in which to render
   * @param direction the direction from the current position that can be thought of as the arrow
   * keys typically found on a keyboard. This will be one of the following values: <ul>
   * <li>SwingConstants.WEST <li>SwingConstants.EAST <li>SwingConstants.NORTH
   * <li>SwingConstants.SOUTH </ul>
   * @return the location within the model that best represents the next location visual position
   * @throws BadLocationException the given position is not a valid position within the document
   * @throws IllegalArgumentException if <code>direction</code> doesn't have one of the legal values
   * above
   */
  public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a,
      int direction, Position.Bias[] biasRet)
      throws BadLocationException {
    if (pos < -1) {
      // -1 is a reserved value, see the code below
      throw new BadLocationException("Invalid position", pos);
    }

    biasRet[0] = Position.Bias.Forward;
    switch (direction) {
      case NORTH:
      case SOUTH: {
        if (pos == -1) {
          pos = (direction == NORTH) ? Math.max(0, getEndOffset() - 1) :
              getStartOffset();
          break;
        }
        JTextComponent target = (JTextComponent) getContainer();
        Caret c = (target != null) ? target.getCaret() : null;
        // YECK! Ideally, the x location from the magic caret position
        // would be passed in.
        Point mcp;
        if (c != null) {
          mcp = c.getMagicCaretPosition();
        } else {
          mcp = null;
        }
        int x;
        if (mcp == null) {
          Rectangle loc = target.modelToView(pos);
          x = (loc == null) ? 0 : loc.x;
        } else {
          x = mcp.x;
        }
        if (direction == NORTH) {
          pos = Utilities.getPositionAbove(target, pos, x);
        } else {
          pos = Utilities.getPositionBelow(target, pos, x);
        }
      }
      break;
      case WEST:
        if (pos == -1) {
          pos = Math.max(0, getEndOffset() - 1);
        } else {
          pos = Math.max(0, pos - 1);
        }
        break;
      case EAST:
        if (pos == -1) {
          pos = getStartOffset();
        } else {
          pos = Math.min(pos + 1, getDocument().getLength());
        }
        break;
      default:
        throw new IllegalArgumentException("Bad direction: " + direction);
    }
    return pos;
  }

  /**
   * Provides a mapping, for a given character,
   * from the document model coordinate space
   * to the view coordinate space.
   *
   * @param pos the position of the desired character (&gt;=0)
   * @param a the area of the view, which encompasses the requested character
   * @param b the bias toward the previous character or the next character represented by the
   * offset, in case the position is a boundary of two views; <code>b</code> will have one of these
   * values: <ul> <li> <code>Position.Bias.Forward</code> <li> <code>Position.Bias.Backward</code>
   * </ul>
   * @return the bounding box, in view coordinate space, of the character at the specified position
   * @throws BadLocationException if the specified position does not represent a valid location in
   * the associated document
   * @throws IllegalArgumentException if <code>b</code> is not one of the legal
   * <code>Position.Bias</code> values listed above
   * @see View#viewToModel
   */
  public abstract Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException;

  /**
   * Provides a mapping, for a given region,
   * from the document model coordinate space
   * to the view coordinate space. The specified region is
   * created as a union of the first and last character positions.
   *
   * @param p0 the position of the first character (&gt;=0)
   * @param b0 the bias of the first character position, toward the previous character or the next
   * character represented by the offset, in case the position is a boundary of two views;
   * <code>b0</code> will have one of these values: <ul style="list-style-type:none"> <li>
   * <code>Position.Bias.Forward</code> <li> <code>Position.Bias.Backward</code> </ul>
   * @param p1 the position of the last character (&gt;=0)
   * @param b1 the bias for the second character position, defined one of the legal values shown
   * above
   * @param a the area of the view, which encompasses the requested region
   * @return the bounding box which is a union of the region specified by the first and last
   * character positions
   * @throws BadLocationException if the given position does not represent a valid location in the
   * associated document
   * @throws IllegalArgumentException if <code>b0</code> or <code>b1</code> are not one of the legal
   * <code>Position.Bias</code> values listed above
   * @see View#viewToModel
   */
  public Shape modelToView(int p0, Position.Bias b0, int p1, Position.Bias b1, Shape a)
      throws BadLocationException {
    Shape s0 = modelToView(p0, a, b0);
    Shape s1;
    if (p1 == getEndOffset()) {
      try {
        s1 = modelToView(p1, a, b1);
      } catch (BadLocationException ble) {
        s1 = null;
      }
      if (s1 == null) {
        // Assume extends left to right.
        Rectangle alloc = (a instanceof Rectangle) ? (Rectangle) a :
            a.getBounds();
        s1 = new Rectangle(alloc.x + alloc.width - 1, alloc.y,
            1, alloc.height);
      }
    } else {
      s1 = modelToView(p1, a, b1);
    }
    Rectangle r0 = s0.getBounds();
    Rectangle r1 = (s1 instanceof Rectangle) ? (Rectangle) s1 :
        s1.getBounds();
    if (r0.y != r1.y) {
      // If it spans lines, force it to be the width of the view.
      Rectangle alloc = (a instanceof Rectangle) ? (Rectangle) a :
          a.getBounds();
      r0.x = alloc.x;
      r0.width = alloc.width;
    }
    r0.add(r1);
    return r0;
  }

  /**
   * Provides a mapping from the view coordinate space to the logical
   * coordinate space of the model.  The <code>biasReturn</code>
   * argument will be filled in to indicate that the point given is
   * closer to the next character in the model or the previous
   * character in the model.
   *
   * @param x the X coordinate &gt;= 0
   * @param y the Y coordinate &gt;= 0
   * @param a the allocated region in which to render
   * @return the location within the model that best represents the given point in the view &gt;= 0.
   * The <code>biasReturn</code> argument will be filled in to indicate that the point given is
   * closer to the next character in the model or the previous character in the model.
   */
  public abstract int viewToModel(float x, float y, Shape a, Position.Bias[] biasReturn);

  /**
   * Gives notification that something was inserted into
   * the document in a location that this view is responsible for.
   * To reduce the burden to subclasses, this functionality is
   * spread out into the following calls that subclasses can
   * reimplement:
   * <ol>
   * <li>{@link #updateChildren updateChildren} is called
   * if there were any changes to the element this view is
   * responsible for.  If this view has child views that are
   * represent the child elements, then this method should do
   * whatever is necessary to make sure the child views correctly
   * represent the model.
   * <li>{@link #forwardUpdate forwardUpdate} is called
   * to forward the DocumentEvent to the appropriate child views.
   * <li>{@link #updateLayout updateLayout} is called to
   * give the view a chance to either repair its layout, to reschedule
   * layout, or do nothing.
   * </ol>
   *
   * @param e the change information from the associated document
   * @param a the current allocation of the view
   * @param f the factory to use to rebuild if the view has children
   * @see View#insertUpdate
   */
  public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) {
    if (getViewCount() > 0) {
      Element elem = getElement();
      DocumentEvent.ElementChange ec = e.getChange(elem);
      if (ec != null) {
        if (!updateChildren(ec, e, f)) {
          // don't consider the element changes they
          // are for a view further down.
          ec = null;
        }
      }
      forwardUpdate(ec, e, a, f);
      updateLayout(ec, e, a);
    }
  }

  /**
   * Gives notification that something was removed from the document
   * in a location that this view is responsible for.
   * To reduce the burden to subclasses, this functionality is
   * spread out into the following calls that subclasses can
   * reimplement:
   * <ol>
   * <li>{@link #updateChildren updateChildren} is called
   * if there were any changes to the element this view is
   * responsible for.  If this view has child views that are
   * represent the child elements, then this method should do
   * whatever is necessary to make sure the child views correctly
   * represent the model.
   * <li>{@link #forwardUpdate forwardUpdate} is called
   * to forward the DocumentEvent to the appropriate child views.
   * <li>{@link #updateLayout updateLayout} is called to
   * give the view a chance to either repair its layout, to reschedule
   * layout, or do nothing.
   * </ol>
   *
   * @param e the change information from the associated document
   * @param a the current allocation of the view
   * @param f the factory to use to rebuild if the view has children
   * @see View#removeUpdate
   */
  public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {
    if (getViewCount() > 0) {
      Element elem = getElement();
      DocumentEvent.ElementChange ec = e.getChange(elem);
      if (ec != null) {
        if (!updateChildren(ec, e, f)) {
          // don't consider the element changes they
          // are for a view further down.
          ec = null;
        }
      }
      forwardUpdate(ec, e, a, f);
      updateLayout(ec, e, a);
    }
  }

  /**
   * Gives notification from the document that attributes were changed
   * in a location that this view is responsible for.
   * To reduce the burden to subclasses, this functionality is
   * spread out into the following calls that subclasses can
   * reimplement:
   * <ol>
   * <li>{@link #updateChildren updateChildren} is called
   * if there were any changes to the element this view is
   * responsible for.  If this view has child views that are
   * represent the child elements, then this method should do
   * whatever is necessary to make sure the child views correctly
   * represent the model.
   * <li>{@link #forwardUpdate forwardUpdate} is called
   * to forward the DocumentEvent to the appropriate child views.
   * <li>{@link #updateLayout updateLayout} is called to
   * give the view a chance to either repair its layout, to reschedule
   * layout, or do nothing.
   * </ol>
   *
   * @param e the change information from the associated document
   * @param a the current allocation of the view
   * @param f the factory to use to rebuild if the view has children
   * @see View#changedUpdate
   */
  public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
    if (getViewCount() > 0) {
      Element elem = getElement();
      DocumentEvent.ElementChange ec = e.getChange(elem);
      if (ec != null) {
        if (!updateChildren(ec, e, f)) {
          // don't consider the element changes they
          // are for a view further down.
          ec = null;
        }
      }
      forwardUpdate(ec, e, a, f);
      updateLayout(ec, e, a);
    }
  }

  /**
   * Fetches the model associated with the view.
   *
   * @return the view model, <code>null</code> if none
   * @see View#getDocument
   */
  public Document getDocument() {
    return elem.getDocument();
  }

  /**
   * Fetches the portion of the model for which this view is
   * responsible.
   *
   * @return the starting offset into the model &gt;= 0
   * @see View#getStartOffset
   */
  public int getStartOffset() {
    return elem.getStartOffset();
  }

  /**
   * Fetches the portion of the model for which this view is
   * responsible.
   *
   * @return the ending offset into the model &gt;= 0
   * @see View#getEndOffset
   */
  public int getEndOffset() {
    return elem.getEndOffset();
  }

  /**
   * Fetches the structural portion of the subject that this
   * view is mapped to.  The view may not be responsible for the
   * entire portion of the element.
   *
   * @return the subject
   * @see View#getElement
   */
  public Element getElement() {
    return elem;
  }

  /**
   * Fetch a <code>Graphics</code> for rendering.
   * This can be used to determine
   * font characteristics, and will be different for a print view
   * than a component view.
   *
   * @return a <code>Graphics</code> object for rendering
   * @since 1.3
   */
  public Graphics getGraphics() {
    // PENDING(prinz) this is a temporary implementation
    Component c = getContainer();
    return c.getGraphics();
  }

  /**
   * Fetches the attributes to use when rendering.  By default
   * this simply returns the attributes of the associated element.
   * This method should be used rather than using the element
   * directly to obtain access to the attributes to allow
   * view-specific attributes to be mixed in or to allow the
   * view to have view-specific conversion of attributes by
   * subclasses.
   * Each view should document what attributes it recognizes
   * for the purpose of rendering or layout, and should always
   * access them through the <code>AttributeSet</code> returned
   * by this method.
   */
  public AttributeSet getAttributes() {
    return elem.getAttributes();
  }

  /**
   * Tries to break this view on the given axis.  This is
   * called by views that try to do formatting of their
   * children.  For example, a view of a paragraph will
   * typically try to place its children into row and
   * views representing chunks of text can sometimes be
   * broken down into smaller pieces.
   * <p>
   * This is implemented to return the view itself, which
   * represents the default behavior on not being
   * breakable.  If the view does support breaking, the
   * starting offset of the view returned should be the
   * given offset, and the end offset should be less than
   * or equal to the end offset of the view being broken.
   *
   * @param axis may be either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
   * @param offset the location in the document model that a broken fragment would occupy &gt;= 0.
   * This would be the starting offset of the fragment returned
   * @param pos the position along the axis that the broken view would occupy &gt;= 0.  This may be
   * useful for things like tab calculations
   * @param len specifies the distance along the axis where a potential break is desired &gt;= 0
   * @return the fragment of the view that represents the given span, if the view can be broken.  If
   * the view doesn't support breaking behavior, the view itself is returned.
   * @see ParagraphView
   */
  public View breakView(int axis, int offset, float pos, float len) {
    return this;
  }

  /**
   * Creates a view that represents a portion of the element.
   * This is potentially useful during formatting operations
   * for taking measurements of fragments of the view.  If
   * the view doesn't support fragmenting (the default), it
   * should return itself.
   *
   * @param p0 the starting offset &gt;= 0.  This should be a value greater or equal to the element
   * starting offset and less than the element ending offset.
   * @param p1 the ending offset &gt; p0.  This should be a value less than or equal to the elements
   * end offset and greater than the elements starting offset.
   * @return the view fragment, or itself if the view doesn't support breaking into fragments
   * @see LabelView
   */
  public View createFragment(int p0, int p1) {
    return this;
  }

  /**
   * Determines how attractive a break opportunity in
   * this view is.  This can be used for determining which
   * view is the most attractive to call <code>breakView</code>
   * on in the process of formatting.  A view that represents
   * text that has whitespace in it might be more attractive
   * than a view that has no whitespace, for example.  The
   * higher the weight, the more attractive the break.  A
   * value equal to or lower than <code>BadBreakWeight</code>
   * should not be considered for a break.  A value greater
   * than or equal to <code>ForcedBreakWeight</code> should
   * be broken.
   * <p>
   * This is implemented to provide the default behavior
   * of returning <code>BadBreakWeight</code> unless the length
   * is greater than the length of the view in which case the
   * entire view represents the fragment.  Unless a view has
   * been written to support breaking behavior, it is not
   * attractive to try and break the view.  An example of
   * a view that does support breaking is <code>LabelView</code>.
   * An example of a view that uses break weight is
   * <code>ParagraphView</code>.
   *
   * @param axis may be either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
   * @param pos the potential location of the start of the broken view &gt;= 0.  This may be useful
   * for calculating tab positions
   * @param len specifies the relative length from <em>pos</em> where a potential break is desired
   * &gt;= 0
   * @return the weight, which should be a value between ForcedBreakWeight and BadBreakWeight
   * @see LabelView
   * @see ParagraphView
   * @see #BadBreakWeight
   * @see #GoodBreakWeight
   * @see #ExcellentBreakWeight
   * @see #ForcedBreakWeight
   */
  public int getBreakWeight(int axis, float pos, float len) {
    if (len > getPreferredSpan(axis)) {
      return GoodBreakWeight;
    }
    return BadBreakWeight;
  }

  /**
   * Determines the resizability of the view along the
   * given axis.  A value of 0 or less is not resizable.
   *
   * @param axis may be either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
   * @return the weight
   */
  public int getResizeWeight(int axis) {
    return 0;
  }

  /**
   * Sets the size of the view.  This should cause
   * layout of the view along the given axis, if it
   * has any layout duties.
   *
   * @param width the width &gt;= 0
   * @param height the height &gt;= 0
   */
  public void setSize(float width, float height) {
  }

  /**
   * Fetches the container hosting the view.  This is useful for
   * things like scheduling a repaint, finding out the host
   * components font, etc.  The default implementation
   * of this is to forward the query to the parent view.
   *
   * @return the container, <code>null</code> if none
   */
  public Container getContainer() {
    View v = getParent();
    return (v != null) ? v.getContainer() : null;
  }

  /**
   * Fetches the <code>ViewFactory</code> implementation that is feeding
   * the view hierarchy.  Normally the views are given this
   * as an argument to updates from the model when they
   * are most likely to need the factory, but this
   * method serves to provide it at other times.
   *
   * @return the factory, <code>null</code> if none
   */
  public ViewFactory getViewFactory() {
    View v = getParent();
    return (v != null) ? v.getViewFactory() : null;
  }

  /**
   * Returns the tooltip text at the specified location. The default
   * implementation returns the value from the child View identified by
   * the passed in location.
   *
   * @see JTextComponent#getToolTipText
   * @since 1.4
   */
  public String getToolTipText(float x, float y, Shape allocation) {
    int viewIndex = getViewIndex(x, y, allocation);
    if (viewIndex >= 0) {
      allocation = getChildAllocation(viewIndex, allocation);
      Rectangle rect = (allocation instanceof Rectangle) ?
          (Rectangle) allocation : allocation.getBounds();
      if (rect.contains(x, y)) {
        return getView(viewIndex).getToolTipText(x, y, allocation);
      }
    }
    return null;
  }

  /**
   * Returns the child view index representing the given position in
   * the view. This iterates over all the children returning the
   * first with a bounds that contains <code>x</code>, <code>y</code>.
   *
   * @param x the x coordinate
   * @param y the y coordinate
   * @param allocation current allocation of the View.
   * @return index of the view representing the given location, or -1 if no view represents that
   * position
   * @since 1.4
   */
  public int getViewIndex(float x, float y, Shape allocation) {
    for (int counter = getViewCount() - 1; counter >= 0; counter--) {
      Shape childAllocation = getChildAllocation(counter, allocation);

      if (childAllocation != null) {
        Rectangle rect = (childAllocation instanceof Rectangle) ?
            (Rectangle) childAllocation : childAllocation.getBounds();

        if (rect.contains(x, y)) {
          return counter;
        }
      }
    }
    return -1;
  }

  /**
   * Updates the child views in response to receiving notification
   * that the model changed, and there is change record for the
   * element this view is responsible for.  This is implemented
   * to assume the child views are directly responsible for the
   * child elements of the element this view represents.  The
   * <code>ViewFactory</code> is used to create child views for each element
   * specified as added in the <code>ElementChange</code>, starting at the
   * index specified in the given <code>ElementChange</code>.  The number of
   * child views representing the removed elements specified are
   * removed.
   *
   * @param ec the change information for the element this view is responsible for.  This should not
   * be <code>null</code> if this method gets called
   * @param e the change information from the associated document
   * @param f the factory to use to build child views
   * @return whether or not the child views represent the child elements of the element this view is
   * responsible for.  Some views create children that represent a portion of the element they are
   * responsible for, and should return false.  This information is used to determine if views in
   * the range of the added elements should be forwarded to or not
   * @see #insertUpdate
   * @see #removeUpdate
   * @see #changedUpdate
   * @since 1.3
   */
  protected boolean updateChildren(DocumentEvent.ElementChange ec,
      DocumentEvent e, ViewFactory f) {
    Element[] removedElems = ec.getChildrenRemoved();
    Element[] addedElems = ec.getChildrenAdded();
    View[] added = null;
    if (addedElems != null) {
      added = new View[addedElems.length];
      for (int i = 0; i < addedElems.length; i++) {
        added[i] = f.create(addedElems[i]);
      }
    }
    int nremoved = 0;
    int index = ec.getIndex();
    if (removedElems != null) {
      nremoved = removedElems.length;
    }
    replace(index, nremoved, added);
    return true;
  }

  /**
   * Forwards the given <code>DocumentEvent</code> to the child views
   * that need to be notified of the change to the model.
   * If there were changes to the element this view is
   * responsible for, that should be considered when
   * forwarding (i.e. new child views should not get
   * notified).
   *
   * @param ec changes to the element this view is responsible for (may be <code>null</code> if
   * there were no changes).
   * @param e the change information from the associated document
   * @param a the current allocation of the view
   * @param f the factory to use to rebuild if the view has children
   * @see #insertUpdate
   * @see #removeUpdate
   * @see #changedUpdate
   * @since 1.3
   */
  protected void forwardUpdate(DocumentEvent.ElementChange ec,
      DocumentEvent e, Shape a, ViewFactory f) {
    calculateUpdateIndexes(e);

    int hole0 = lastUpdateIndex + 1;
    int hole1 = hole0;
    Element[] addedElems = (ec != null) ? ec.getChildrenAdded() : null;
    if ((addedElems != null) && (addedElems.length > 0)) {
      hole0 = ec.getIndex();
      hole1 = hole0 + addedElems.length - 1;
    }

    // forward to any view not in the forwarding hole
    // formed by added elements (i.e. they will be updated
    // by initialization.
    for (int i = firstUpdateIndex; i <= lastUpdateIndex; i++) {
      if (!((i >= hole0) && (i <= hole1))) {
        View v = getView(i);
        if (v != null) {
          Shape childAlloc = getChildAllocation(i, a);
          forwardUpdateToView(v, e, childAlloc, f);
        }
      }
    }
  }

  /**
   * Calculates the first and the last indexes of the child views
   * that need to be notified of the change to the model.
   *
   * @param e the change information from the associated document
   */
  void calculateUpdateIndexes(DocumentEvent e) {
    int pos = e.getOffset();
    firstUpdateIndex = getViewIndex(pos, Position.Bias.Forward);
    if (firstUpdateIndex == -1 && e.getType() == DocumentEvent.EventType.REMOVE &&
        pos >= getEndOffset()) {
      // Event beyond our offsets. We may have represented this, that is
      // the remove may have removed one of our child Elements that
      // represented this, so, we should forward to last element.
      firstUpdateIndex = getViewCount() - 1;
    }
    lastUpdateIndex = firstUpdateIndex;
    View v = (firstUpdateIndex >= 0) ? getView(firstUpdateIndex) : null;
    if (v != null) {
      if ((v.getStartOffset() == pos) && (pos > 0)) {
        // If v is at a boundary, forward the event to the previous
        // view too.
        firstUpdateIndex = Math.max(firstUpdateIndex - 1, 0);
      }
    }
    if (e.getType() != DocumentEvent.EventType.REMOVE) {
      lastUpdateIndex = getViewIndex(pos + e.getLength(), Position.Bias.Forward);
      if (lastUpdateIndex < 0) {
        lastUpdateIndex = getViewCount() - 1;
      }
    }
    firstUpdateIndex = Math.max(firstUpdateIndex, 0);
  }

  /**
   * Updates the view to reflect the changes.
   */
  void updateAfterChange() {
    // Do nothing by default. Should be overridden in subclasses, if any.
  }

  /**
   * Forwards the <code>DocumentEvent</code> to the give child view.  This
   * simply messages the view with a call to <code>insertUpdate</code>,
   * <code>removeUpdate</code>, or <code>changedUpdate</code> depending
   * upon the type of the event.  This is called by
   * {@link #forwardUpdate forwardUpdate} to forward
   * the event to children that need it.
   *
   * @param v the child view to forward the event to
   * @param e the change information from the associated document
   * @param a the current allocation of the view
   * @param f the factory to use to rebuild if the view has children
   * @see #forwardUpdate
   * @since 1.3
   */
  protected void forwardUpdateToView(View v, DocumentEvent e,
      Shape a, ViewFactory f) {
    DocumentEvent.EventType type = e.getType();
    if (type == DocumentEvent.EventType.INSERT) {
      v.insertUpdate(e, a, f);
    } else if (type == DocumentEvent.EventType.REMOVE) {
      v.removeUpdate(e, a, f);
    } else {
      v.changedUpdate(e, a, f);
    }
  }

  /**
   * Updates the layout in response to receiving notification of
   * change from the model.  This is implemented to call
   * <code>preferenceChanged</code> to reschedule a new layout
   * if the <code>ElementChange</code> record is not <code>null</code>.
   *
   * @param ec changes to the element this view is responsible for (may be <code>null</code> if
   * there were no changes)
   * @param e the change information from the associated document
   * @param a the current allocation of the view
   * @see #insertUpdate
   * @see #removeUpdate
   * @see #changedUpdate
   * @since 1.3
   */
  protected void updateLayout(DocumentEvent.ElementChange ec,
      DocumentEvent e, Shape a) {
    if ((ec != null) && (a != null)) {
      // should damage more intelligently
      preferenceChanged(null, true, true);
      Container host = getContainer();
      if (host != null) {
        host.repaint();
      }
    }
  }

  /**
   * The weight to indicate a view is a bad break
   * opportunity for the purpose of formatting.  This
   * value indicates that no attempt should be made to
   * break the view into fragments as the view has
   * not been written to support fragmenting.
   *
   * @see #getBreakWeight
   * @see #GoodBreakWeight
   * @see #ExcellentBreakWeight
   * @see #ForcedBreakWeight
   */
  public static final int BadBreakWeight = 0;

  /**
   * The weight to indicate a view supports breaking,
   * but better opportunities probably exist.
   *
   * @see #getBreakWeight
   * @see #BadBreakWeight
   * @see #ExcellentBreakWeight
   * @see #ForcedBreakWeight
   */
  public static final int GoodBreakWeight = 1000;

  /**
   * The weight to indicate a view supports breaking,
   * and this represents a very attractive place to
   * break.
   *
   * @see #getBreakWeight
   * @see #BadBreakWeight
   * @see #GoodBreakWeight
   * @see #ForcedBreakWeight
   */
  public static final int ExcellentBreakWeight = 2000;

  /**
   * The weight to indicate a view supports breaking,
   * and must be broken to be represented properly
   * when placed in a view that formats its children
   * by breaking them.
   *
   * @see #getBreakWeight
   * @see #BadBreakWeight
   * @see #GoodBreakWeight
   * @see #ExcellentBreakWeight
   */
  public static final int ForcedBreakWeight = 3000;

  /**
   * Axis for format/break operations.
   */
  public static final int X_AXIS = HORIZONTAL;

  /**
   * Axis for format/break operations.
   */
  public static final int Y_AXIS = VERTICAL;

  /**
   * Provides a mapping from the document model coordinate space
   * to the coordinate space of the view mapped to it. This is
   * implemented to default the bias to <code>Position.Bias.Forward</code>
   * which was previously implied.
   *
   * @param pos the position to convert &gt;= 0
   * @param a the allocated region in which to render
   * @return the bounding box of the given position is returned
   * @throws BadLocationException if the given position does not represent a valid location in the
   * associated document
   * @see View#modelToView
   * @deprecated
   */
  @Deprecated
  public Shape modelToView(int pos, Shape a) throws BadLocationException {
    return modelToView(pos, a, Position.Bias.Forward);
  }


  /**
   * Provides a mapping from the view coordinate space to the logical
   * coordinate space of the model.
   *
   * @param x the X coordinate &gt;= 0
   * @param y the Y coordinate &gt;= 0
   * @param a the allocated region in which to render
   * @return the location within the model that best represents the given point in the view &gt;= 0
   * @see View#viewToModel
   * @deprecated
   */
  @Deprecated
  public int viewToModel(float x, float y, Shape a) {
    sharedBiasReturn[0] = Position.Bias.Forward;
    return viewToModel(x, y, a, sharedBiasReturn);
  }

  // static argument available for viewToModel calls since only
  // one thread at a time may call this method.
  static final Position.Bias[] sharedBiasReturn = new Position.Bias[1];

  private View parent;
  private Element elem;

  /**
   * The index of the first child view to be notified.
   */
  int firstUpdateIndex;

  /**
   * The index of the last child view to be notified.
   */
  int lastUpdateIndex;

};
